#include "stdafx.h"
#include "solver.h"
#include "MLP.h"
#include "layer.h"
#include "neuron.h"
#include "Layer Form.h"

namespace nnse
{
	extern io_module io_m;

	// Constructor and Destructor

	template <class T, class U> layer<T, U>::layer(MLP<T, U>^ p, unsigned int l_n)
	: parent(p), number(l_n)
	{
		io_m.startup_stream << "Constructing layer " << get_number() << std::endl;
		number_of_neurons=0;
		neurons=nullptr;
		layer_form=nullptr;
	}

	template <class T, class U> layer<T, U>::~layer()
	{
		io_m.startup_stream << "Destructing layer " << get_number() << std::endl;
	}

	// Accessor Functions

	template <class T, class U> inline problem<T, U>^ layer<T, U>::get_parent_problem()
	{
		return get_parent_MLP()->get_parent_solver()->get_parent_problem();
	}

	template <class T, class U> inline MLP<T, U>^ layer<T, U>::get_parent_MLP()
	{
		return parent;
	}

	template <class T, class U> inline unsigned int layer<T, U>::get_number()
	{
		return number;
	}

	template <class T, class U> inline void layer<T, U>::put_number(unsigned int n)
	{
		number=n;
	}

	template <class T, class U> inline unsigned int layer<T, U>::get_number_of_neurons()
	{
		return number_of_neurons;
	}

	template <class T, class U> inline void layer<T, U>::put_number_of_neurons(unsigned int n_n)
	{
		number_of_neurons=n_n;
	}

	template <class T, class U> inline neuron<T, U>% layer<T, U>::get_neuron(unsigned int n_n)
	{
		if (neurons && n_n>=0 && n_n<neurons->Length)
		{
			return *neurons[n_n];
		}
		else
		{
			throw std::out_of_range("\nError in layer<T, U>::get_neuron");
		}
	}

	// Worker Functions

	template <class T, class U> void layer<T, U>::forward(layer<T, U>% next_layer, bool update_flag)
	{
		for (unsigned int n_n=0; n_n<get_number_of_neurons(); n_n++)
		{
			get_neuron(n_n).forward(next_layer, update_flag);
		}
	}

	template <class T, class U> U layer<T, U>::reverse(layer<T, U>% next_layer)
	{
		U E=0.0;
		for (unsigned int n_n=0; n_n<get_number_of_neurons(); n_n++)
		{
			E+=get_neuron(n_n).reverse(next_layer);
		}
		return E;
	}

	// GUI Functions

	template <class T, class U> void layer<T, U>::display_form()
	{
		if (!layer_form)
		{
			layer_form=gcnew SimpleGUI::Layer<T, U>(this);
		}
		layer_form->Show();
		layer_form->update_form();
	}

	template <class T, class U> void layer<T, U>::invalidate_form()
	{
		layer_form=nullptr;
	}

	template <class T, class U> void layer<T, U>::display_neurons()
	{
		for (unsigned int n_n=0; n_n<get_number_of_neurons(); n_n++)
		{
			get_neuron(n_n).display_form();
		}
	}

	// Serialisation Functions

	template <class T, class U> void layer<T, U>::read(std::wifstream& in)
	{
		unsigned int number;
		in >> number;
		put_number(number);
		unsigned int number_of_neurons;
		in >> number_of_neurons;
		put_number_of_neurons(number_of_neurons);
		for (unsigned int n_n=0; n_n<get_number_of_neurons(); n_n++)
		{
			in >> get_neuron(n_n);
		}
	}

	template <class T, class U> void layer<T, U>::write(std::wofstream& out)
	{
		out << get_number() << " ";
		out << get_number_of_neurons() << " ";
		for (unsigned int n_n=0; n_n<get_number_of_neurons(); n_n++)
		{
			out << get_neuron(n_n);
		}
	}

	template <class T, class U> void layer<T, U>::display(std::wofstream& out)
	{
		out << "Number: " << get_number() << std::endl;
		out << "Number of Neurons: " << get_number_of_neurons() << std::endl;
		for (unsigned int n_n=0; n_n<get_number_of_neurons(); n_n++)
		{
			out << get_neuron(n_n);
		}
	}

	template <class T, class U> std::wifstream& operator>>(std::wifstream& in, layer<T, U>% l)
	{
		if (in.good())
		{
			l.read(in);
			return in;
		}
		else
		{
			throw std::ios_base::failure("\nError in wifstream& >> layer<T, U>%");
		}
	}

	template <class T, class U> std::wofstream& operator<<(std::wofstream& out, layer<T, U>% l)
	{
		if (out.good())
		{
			l.write(out);
			return out;
		}
		else
		{
			throw std::ios_base::failure("\nError in wofstream& << layer<T, U>%");
		}
	}

	// Explicit Instantiation

	template ref class layer<bool, double>;
	template ref class layer<unsigned char, double>;
	template std::wifstream& operator>>(std::wifstream& in, layer<bool, double>% l);
	template std::wifstream& operator>>(std::wifstream& in, layer<unsigned char, double>% l);
	template std::wofstream& operator<<(std::wofstream& out, layer<bool, double>% l);
	template std::wofstream& operator<<(std::wofstream& out, layer<unsigned char, double>% l);
}
